// Copyright (C) 2011 by Christopher Diggins
// Usage is permitted under the terms of the MIT License 1.0. 	

/** \file 
	This file contains functions for working with Val* data types.
	\see \ref val
*/

/** Initializes a value, setting the type. */
Val* val_init(Val* x, int type) {
	memset(x, 0, sizeof(Val));
	x->type = type;
	return x;
}

/** Creates a new Val from an int value. */
Val* val_from_int_impl(VM* vm, int x) {
	Val* r = val_new(IntType);
	val_to_int(r) = x;
	return r;
}

/** Creates a new Val from a float value. */
Val* val_from_float_impl(VM* vm, float x) {
	Val* r = val_new(FloatType);
	val_to_float(r) = x;
	return r;
}

/** Creates a new Val from a double value. */
Val* val_from_double_impl(VM* vm, double x) {
	Val* r = val_new(DoubleType);
	val_to_double(r) = x;
	return r;
}

/** Adds a reference to the value or duplicates it, depending 
	on the nature of the object. */
Val* val_dup_impl(VM* vm, Val* x) {
	if (val_has_array(x))  
		return val_add_ref(x);
	else
	{
		Val* r = val_new(x->type);
		*r = *x;
		r->refs = 1;
		return r;
	}
}

/** Adds a new reference to a Val. */
Val* val_add_ref_impl(VM* vm, Val* x) {
	if (!val_max_refs(x)) {
		x->refs++;
		val_clear_flag(x, MaybeGarbageFlag);
	}
	return x;
}

/** Deletes a Val (regardless of the type) and all referenced objects. */
void val_del_impl(VM* vm, Val* x) {
	if (val_has_flag(x, DeletedFlag | DeletingFlag))
		return;
	val_set_flag(x, DeletingFlag);
	
	if (val_is_obj(x) && val_has_array(x))
	{
		Val** begin = obj_begin(x);
		Val** end = obj_end(x);
		for (; begin != end; ++begin)
			val_del_ref_impl(vm, *begin);
		array_free(obj_array(x));
	}
	else if (val_is_table(x))
	{
		KeyVal* begin = table_begin(x);
		KeyVal* end	= table_end(x);
		for (; begin != end; ++begin)
			if (table_slot_occupied(begin))
				val_del_ref(begin->val);
		array_free(obj_array(x));
	}
	else if (val_is_pair(x))
	{
		val_del_ref(pair_first(x));
		val_del_ref(pair_second(x));
	}
	val_clear_flag(x, DeletingFlag);
	val_set_flag(x, DeletedFlag);
	mem_val_free(&vm->mem, x);
}

/** Removes a reference from a value deleting it if needed. */
void val_del_ref_impl(VM* vm, Val* x) {
	if (val_has_flag(x, DeletingFlag | DeletedFlag | ConstantFlag)) 
		return;
	if (val_max_refs(x))
		return;	
	if (!--x->refs) 
		val_del_impl(vm, x);
	else if (x->type == ObjectType)
		val_set_flag(x, MaybeGarbageFlag);
}

/** Creates a string representation of a Value in a char buffer. */
int val_to_str_buffer(Val* x, char* s, int size) {
	switch (x->type)
	{
		case IntType: return sprintf_s(s, size, "%d", val_to_int(x)); 
		case FloatType: return sprintf_s(s, size, "%f", val_to_float(x)); 
		case DoubleType: return sprintf_s(s, size, "%lf", val_to_double(x)); 
		case TableType: return sprintf_s(s, size, "table(count=%d=%d)", table_count(x), table_size(x)); 
		case ObjectType: return sprintf_s(s, size, "object(vtbl=%x, data=%x)", obj_vtbl(x), obj_array(x)); 
		case FunctionType: return sprintf_s(s, size, "function(code=%x, args=%x)", fun_code(x), fun_args(x)); 
		case ByteType: return sprintf_s(s, size, "%d", x->data.ByteData.val); 
		case MarkerType: return sprintf_s(s, size, "marker %x", x); 
		case StoreType: return sprintf_s(s, size, "store %x", x); 
		case NativeFunType: return sprintf_s(s, size, "native_function %x", x); 
		case VByteType: return sprintf_s(s, size, "byte vector %x", x);
		case VIntType: return sprintf_s(s, size, "int vector %x", x);
		case VFloatType: return sprintf_s(s, size, "float vector %x", x);
		case VDoubleType: return sprintf_s(s, size, "double vector %x", x);
		default: return sprintf_s(s, size, "unknown %x", x); break;
	}		
}

/** Converts a value to a string representation. */
Val* val_to_str_impl(VM* vm, Val* x) {
	char s[100];
	int i;
	Val* r;
	int n = val_to_str_buffer(x, s, sizeof(s));
	assert(n < sizeof(s));
	r = vec_new(VByteType, n);
	for (i=0; i < n; ++i)
		vec_at(byte, r, i) = s[i];
	return r;
}

//=========================================================
// Object functions

/** Removes the top item from a list. */
Val* obj_pull_impl(VM* vm, Val* list) {
	Val* r = obj_top(list);
	array_pop(obj_array(list));
	return r;
}

/** Creates a new function that points to some code. */
Val* fun_new_impl(VM* vm, byte* f) {
	Val* r = val_new(FunctionType);
	fun_code(r) = f;
	return r;
}

